
let { Tapable, SyncHook } = require('tapable');
const NormalModuleFactory = require('./NormalModuleFactory');
const normalModuleFactory = new NormalModuleFactory();
const Parser = require('./Parser');
const parser = new Parser();
const path = require('path');


class Compilation extends Tapable {
    constructor(compiler) {
        super()
        this.compiler = compiler;//编译器对象 
        this.options = compiler.options;// 选项一样
        this.context = compiler.context;//根目录
        this.inputFileSystem = compiler.inputFileSystem;//读取文件模块fs
        this.outputFileSystem = compiler.outputFileSystem;//写入文件的模块fs
        this.entries = [];//入口模块的数组,这里放着所有的入口模块
        this.modules = [];//模块的数组,这里放着所有的模块
        this._modules = {};//key是模块ID ,值是模块对象
        this.chunks = [];//这里放着所有代码块
        this.files = [];//这里放着本次编译所有的产出的文件名
        this.assets = {};//存放 着生成资源 key是文件名 值是文件的内容
        this.vendors = [];//放着所有的第三方模块 isarray
        this.commons = [];//这里放着同时被多个代码块加载的模块  title.js
        this.moduleCount= {};//可以记录每个模块被代码块引用的次数,如果大于等于2,就分离出到commons里
        this.hooks = {
          succeedModule: new SyncHook(['module']), //当你成功构建完成一个模块后就会触发此钩子执行
          seal:new SyncHook(),
          beforeChunks:new SyncHook(),
          afterChunks:new SyncHook()
        }
    }
    /**
     * 开始编译一个新的入口
     * @param {*} context  根目录 
     * @param {*} entry 入口模块的相对路径 ./src/index.js
     * @param {*} name 入口的名字 main
     * @param {*} callback 编译完成的回调
     */
    addEntry(context, entry, name, finalCallback) {
        this._addModuleChain(context, entry, name, false, (err, module) => {
            finalCallback(err, module);
        });
    }
    /**
     * 
     * @param {*} context 根目录 
     * @param {*} rawRequest 入口模块的相对路径 ./src/index.js
     * @param {*} name 入口的名字 main
     * @param {*} async 同步还是异步
     * @param {*} callback 回调函数
     */
    _addModuleChain(context, rawRequest, name, async, callback) {
        let entryModule = normalModuleFactory.create({
            name, //main
            context, //根目录
            rawRequest, //入口模块的相对路径 ./src/index.js
            parser, 
            resource:path.posix.join(context,rawRequest), //入口的绝对路径
        });
        this.entries.push(entryModule) //给入口模块数组添加一个模块
        this.modules.push(entryModule) //给普通模块数组添加一个模块
        const afterBuild = (err) => {
            return callback(err,entryModule)
        }
        this.buildModule(entryModule, afterBuild);
    }
    /**
     * 编译模块
     * @param {*} module 要编译的模块
     * @param {*} afterBuild 编译完成后的后的回调
     */
    buildModule(module, afterBuild) {
        //模块的真正的编译逻辑其实是放在module内部完成
        module.build(this, (err) => {
            //走到这里意味着一个module模块已经编译完成了
            this.hooks.succeedModule.call(module);
            afterBuild(err, module);
        });
    }
    
}
module.exports = Compilation